#include "MockTable.h"
#include "memdb/error/MemdbError.h"

namespace memdb {
namespace impl {
namespace mock {

MockTable::~MockTable() {
}

MockTable::MockTable() {
}

size_t MockTable::count() const {
  return _records.size();
}

std::error_code MockTable::get(const KeyType & key, ValueType & value) const {
  auto && it = _records.find(key);
  if (it != _records.end()) {
    value = it->second;
    return memdb::error::ok;
  }
  return memdb::error::key_not_exists;
}

std::error_code MockTable::insert(const KeyType & key,  const ValueType & value) {
  auto ret = _records.insert({key, value});
  if (ret.second) {
    return memdb::error::ok;
  }
  return memdb::error::key_exists;
}

std::error_code MockTable::remove(const KeyType & key) {
  auto && it = _records.find(key);
  if (it != _records.end()) {
    _records.erase(it);
    return memdb::error::ok;
  }
  return memdb::error::key_not_exists;
}

std::error_code MockTable::update(const KeyType & key, const ValueType & value) {
  auto && it = _records.find(key);
  if (it != _records.end()) {
    _records[key] = value;
    return memdb::error::ok;
  }
  return memdb::error::key_not_exists;
}

std::error_code MockTable::replace(const KeyType & key, const ValueType & value) {
  _records[key] = value;
  return memdb::error::ok;
}

std::error_code MockTable::update(const KeyType & key, const ValueType & value, ValueType & old) {
  auto && it = _records.find(key);
  if (it != _records.end()) {
    old = _records[key];
    _records[key] = value;
    return memdb::error::ok;
  }
  return memdb::error::key_not_exists;
}

std::pair<std::error_code, bool> MockTable::replace(const KeyType & key, const ValueType & value, ValueType & old) {
  bool is_replace = true;
  auto && it = _records.find(key);
  if (it != _records.end()) {
    old = _records[key];
  }
  else {
    is_replace = false;
  }
  _records[key] = value;
  return std::make_pair(memdb::error::ok, is_replace);
}

using Item = ITable::Item;
using KeyType = ITable::KeyType;
using ValueType = ITable::ValueType;
using iterator = std::map<KeyType, ValueType>::const_iterator;

class TableIterator : public ITable::Iterator {
public:
  explicit TableIterator(const iterator & begin, const iterator & end)
  :_it(begin)
  ,_end(end)
  {
  }
  ~TableIterator(){}

  std::optional<Item> next() override {
    if(_it != _end){
      auto * item = (Item)(&(*_it++));
      return std::make_optional<Item>(item);
    }
    return std::nullopt; 
  }

protected:
  iterator _it;
  const iterator _end;
};

std::unique_ptr<ITable::Iterator> MockTable::iterator() const {
  return std::unique_ptr<Iterator>(new TableIterator(_records.cbegin(), _records.cend()));
}

class TableRangeIterator : public ITable::Iterator {
public:
  explicit TableRangeIterator(const iterator & begin, const iterator & end, size_t len)
  : _it(begin, end)
  , _len(len)
  {
  }

  ~TableRangeIterator(){}
  
  std::optional<Item> next() override {
    if(_len-- > 0){
      return _it.next();
    }
    return std::nullopt; 
  }
private:
  TableIterator _it;
  size_t _len;
};

std::unique_ptr<ITable::Iterator> __range(iterator && begin, const iterator & end, size_t index, size_t len) {
  for(; begin != end && index > 0; ++begin, --index) {}
  return std::unique_ptr<ITable::Iterator>(new TableRangeIterator(begin, end, len));
}

std::unique_ptr<ITable::Iterator> MockTable::range(size_t index, size_t len) const {
  return __range(_records.begin(), _records.end(), index, len);
}

std::unique_ptr<ITable::Iterator> MockTable::findRange(const KeyType & key, size_t offset, size_t len) const {
  return __range(_records.find(key), _records.end(), offset, len);
}

}//namespace mock
}//namespace impl
}//namespace memdb
